#include "game.h"
#include "display.c"
#include "btns.c"
#include "uart.c"
#include "timer.c"

unsigned int game_frame_count = 0;
#define GAME_FRAME 10
/**
 * data 128 字节  声明变量默认的存放位置
 * idata 128 字节
 */
unsigned int idata GAME_ZONE[19];

/**
 * 0x2004  中间的0代表10列游戏区域，1代表墙壁用于碰撞检查
 * 对应屏幕从上到下，对应数组下标0~15
 * 16~19全部填充1，用于碰撞检测，防止数组越界
 */
void init_game_zone()
{
    unsigned char i;
    for (i = 0; i < 16; i++)
    {
        GAME_ZONE[i] = 0x2004;
    }

    for (i = 16; i < 19; i++)
    {
        GAME_ZONE[i] = 0xFFFF;
    }
}

/**
 * GAME_ZONE 里面所有的数据显示到屏幕上
 */
void display_refresh_game_zone()
{
    // 游戏区行数是16行，每两行一起进行显示
    unsigned char row;
    unsigned char col;
    unsigned char top;
    unsigned char bottom;
    unsigned char display_bytes[4];
    // 每两行一起处理，一共需要处理8次
    for (row = 0; row < 8; row++)
    {
        // 一共处理10列
        for (col = 0; col < 10; col++)
        {
            // 第一行
            // 存储的数据，最左边的一位是第12位
            top = (((GAME_ZONE[row * 2] >> (12 - col)) & 1) == 1);
            // 第二行
            bottom = (((GAME_ZONE[row * 2 + 1] >> (12 - col)) & 1) == 1);
            fill_display_block_arr(top, bottom, display_bytes);
            lcd_display_bytes_in_page(row, 31 + col * 4, display_bytes, 4);
        }
    }
}

/**
 * shape数据是int，4位一组，对应一个GAME_ZONE元素
 * 写入连续的4个数组元素，相当于连续写入4行
 */
void add_shape_to_game_zone()
{
    unsigned char i;
    unsigned int shape_4_byte_data;
    // 起始行，对应shape.y 操作到shape.y+3
    // GAME_ZONE元素19个，不会越界
    for (i = 0; i < 4; i++)
    {
        // 每行对应 shape的4位数据
        // shape的每一个bit和 GAME_ZONE 的指定bit对位
        // 做或运算，只有shape中1的数据写入到game zone，防止已有数据被擦除

        // abcd efgh ijkl mnop

        // abcd
        // efgh
        // ijkl
        // mnop

        // xx1? ???? ???? ?1xx
        //    a bcd

        // xx1? ???? ???? ?1xx
        // xx1? ???? ???? ?1xx
        // xx1? ???? ???? ?1xx

        // step1: 取高四位 >> 12
        // 0000 0000 0000 abcd
        // shape_4_byte_data = (game_shape.shape >> 12) & 0x000F;
        shape_4_byte_data = (game_shape.shape >> (12 - 4 * i)) & 0x000F;

        // step2: 左移对齐
        // xx1? ???? ???? ?1xx
        //    a bcd
        shape_4_byte_data <<= (9 - game_shape.x);
        GAME_ZONE[game_shape.y + i] |= shape_4_byte_data;
    }
}

void remove_shape_to_game_zone()
{
    unsigned char i;
    unsigned int shape_4_byte_data;
    for (i = 0; i < 4; i++)
    {
        shape_4_byte_data = (game_shape.shape >> (12 - 4 * i)) & 0x000F;
        shape_4_byte_data <<= (9 - game_shape.x);
        GAME_ZONE[game_shape.y + i] &= ~shape_4_byte_data;
    }
}

/**
 * shape数据是否可以放置到game_zone 的x,y位置
 * @return 0：形状不能放置到当前位置
 */
bit game_check_shape_placed(
    unsigned char x,
    unsigned char y,
    unsigned int shape)
{
    unsigned char i;
    unsigned int shape_4_byte_data;

    for (i = 0; i < 4; i++)
    {
        shape_4_byte_data = (shape >> (12 - 4 * i)) & 0x000F;
        shape_4_byte_data <<= (9 - x);
        if ((GAME_ZONE[y + i] & shape_4_byte_data) != 0)
        {
            // 当前位置存在冲突，不能放置
            return 0;
        }
    }
    return 1;
}

void init_shape()
{
    update_timer_seed();
    game_shape.pos = timer_rand() % 4;
    game_shape.type = timer_rand() % 7;
    game_shape.shape = TETRIS_SHAPES[game_shape.type][game_shape.pos];
    update_timer_seed();
    game_shape.next_pos = timer_rand() % 4;
    game_shape.next_type = timer_rand() % 7;
    game_shape.next_shape = TETRIS_SHAPES[game_shape.next_type][game_shape.next_pos];
    game_shape.x = 3;
    game_shape.y = 0;
}

void timer0_ISR() interrupt 1
{
    if (game_frame_count < GAME_FRAME)
    {
        game_frame_count++;
    }
    else
    {
        game_frame_count = 0;
        remove_shape_to_game_zone();
        if (game_check_shape_placed(game_shape.x, game_shape.y + 1, game_shape.shape))
        {
            game_shape.y++;
        }
        else
        {
            add_shape_to_game_zone();
            init_shape();
        }
        add_shape_to_game_zone();
        display_refresh_game_zone();
    }
}

void main()
{
    uart_init();
    timer0_init();
    init_game_zone();
    display_init();
    display_init_game_page();

    init_shape();
    timer0_isr_on();
    while (1)
    {
    }
}